//-----------------------------------------------------------------------------
// File: CGameApp.cpp
//
// Desc: Game Application class, this is the central hub for all app processing
//
// Copyright (c) 1997-2002 Adam Hoult & Gary Simmons. All rights reserved.
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// CGameApp Specific Includes
//-----------------------------------------------------------------------------
#include "..\\Includes\\CGameApp.h"

//-----------------------------------------------------------------------------
// Name : CGameApp () (Constructor)
// Desc : CGameApp Class Constructor
//-----------------------------------------------------------------------------
CGameApp::CGameApp()
{
	// Reset / Clear all required values
    m_hWnd              = NULL;
    m_hdcFrameBuffer    = NULL;
    m_hbmSelectOut      = NULL;
    m_hbmFrameBuffer    = NULL;
}

//-----------------------------------------------------------------------------
// Name : ~CGameApp () (Destructor)
// Desc : CGameApp Class Destructor
//-----------------------------------------------------------------------------
CGameApp::~CGameApp()
{
	// Shut the engine down... THIS WILL CAUSE A CRASH!  DO NOT SHUT DOWN TWICE!
    //ShutDown();
}

//-----------------------------------------------------------------------------
// Name : InitInstance ()
// Desc : Initialises the entire Engine here.
//-----------------------------------------------------------------------------
bool CGameApp::InitInstance( HINSTANCE hInstance, LPCTSTR lpCmdLine, int iCmdShow )
{
	m_hInstance = hInstance;
    // Create the primary display device
    if (!CreateDisplay()) { ShutDown(); return false; }

    // Build Objects
    if (!BuildObjects()) { ShutDown(); return false; }

    // Set up all required game states
    SetupGameState();

    // Success!
	return true;
}

//-----------------------------------------------------------------------------
// Name : CreateDisplay ()
// Desc : Validate and set the current display device plugin, and create the
//        primary display device. Returns false only if we should bail.
//-----------------------------------------------------------------------------
bool CGameApp::CreateDisplay()
{
    LPTSTR  WindowTitle  = _T("Sprinter");
    USHORT  Width        = 800;
    USHORT  Height       = 600;
    HDC     hDC          = NULL;
    RECT    rc;
    
    // Register the new windows window class.
    WNDCLASS			wc;	
	wc.style			= CS_BYTEALIGNCLIENT | CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc		= StaticWndProc;
	wc.cbClsExtra		= 0;
	wc.cbWndExtra		= 0;
	wc.hInstance		= (HINSTANCE)GetModuleHandle(NULL);
	//wc.hIcon			= LoadIcon( wc.hInstance, MAKEINTRESOURCE(IDI_ICON));
	wc.hIcon            = LoadIcon( NULL, IDI_APPLICATION );
	wc.hCursor			= LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground	= (HBRUSH )GetStockObject(NULL_BRUSH);
	wc.lpszMenuName		= NULL;
	wc.lpszClassName	= WindowTitle;
	RegisterClass(&wc);

    // Create the rendering window
	m_hWnd = CreateWindow( WindowTitle, WindowTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 
                           CW_USEDEFAULT, Width, Height, NULL, LoadMenu( wc.hInstance, MAKEINTRESOURCE(IDR_MENU) ),
                           wc.hInstance, this );
	
    // Bail on error
    if (!m_hWnd) return false;

    // Retrieve the final client size of the window
    ::GetClientRect( m_hWnd, &rc );
    m_nViewX      = rc.left;
    m_nViewY      = rc.top;
    m_nViewWidth  = rc.right - rc.left;
    m_nViewHeight = rc.bottom - rc.top;

    // Build the frame buffer
    if (!BuildFrameBuffer( Width, Height )) return false;
    
	// Show the window
	ShowWindow(m_hWnd, SW_SHOW);

    // Success!
    return true;
}

//-----------------------------------------------------------------------------
// Name : BuildFrameBuffer ()
// Desc : Creates the frame buffer memory DC ready for use.
// Note : Destroys the DC / Bitmap when needed, so can be re-used
//-----------------------------------------------------------------------------
bool CGameApp::BuildFrameBuffer( ULONG Width, ULONG Height )
{
	//Wow this function got a lot smaller with the implementation of BackBuffer :p
	m_pBackBuffer = new BackBuffer(m_hWnd, m_nViewWidth, m_nViewHeight);  

	m_SpriteDC    = CreateCompatibleDC(0);

    // Success!!
    return true;
}


//-----------------------------------------------------------------------------
// Name : SetupGameState ()
// Desc : Sets up all the initial states required by the game.
//-----------------------------------------------------------------------------
void CGameApp::SetupGameState()
{
	m_nGameTime = 0.0f;
	MikeUtils::ASSERT(m_StateManager.ChangeMenuState(MenuStates::eMenu_Main), "Menu State change failed!", __FILE__, __LINE__);
}

//-----------------------------------------------------------------------------
// Name : BeginGame ()
// Desc : Signals the beginning of the physical post-initialisation stage.
//        From here on, the game engine has control over processing.
//-----------------------------------------------------------------------------
int CGameApp::BeginGame()
{
    MSG		msg;

    // Start main loop
	while (1) 
    {
        // Did we recieve a message, or are we idling ?
		if ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) 
        {
			if (msg.message == WM_QUIT) break;
			TranslateMessage( &msg );
			DispatchMessage ( &msg );
		} 
        else 
        {
			// Advance Game Frame.
			FrameAdvance();

		} // End If messages waiting
	
    } // Until quit message is receieved

    return 0;
}

//-----------------------------------------------------------------------------
// Name : ShutDown ()
// Desc : Shuts down the game engine, and frees up all resources.
//-----------------------------------------------------------------------------
bool CGameApp::ShutDown()
{
    // Destroy the frame buffer and associated DC's
    if ( m_hdcFrameBuffer && m_hbmFrameBuffer )
    {
        ::SelectObject( m_hdcFrameBuffer, m_hbmSelectOut );
        ::DeleteObject( m_hbmFrameBuffer );
        ::DeleteDC( m_hdcFrameBuffer );

    } // End if

	if(m_SpriteDC)
	{
		::DeleteDC( m_SpriteDC);
	}

    // Destroy the render window
    if ( m_hWnd ) DestroyWindow( m_hWnd );
    
	delete m_pBackBuffer;

	m_pMenu->Deinit();


#ifdef RAND_CRASH_DEBUG
	if(m_pMenu)
		delete m_pMenu;
#endif

    // Clear all variables
    m_hWnd              = NULL;
    m_hbmFrameBuffer    = NULL;
    m_hdcFrameBuffer    = NULL;
    
    // Shutdown Success
    return true;
}

//-----------------------------------------------------------------------------
// Name : StaticWndProc () (Static Callback)
// Desc : This is the main messge pump for ALL display devices, it captures
//        the appropriate messages, and routes them through to the application
//        class for which it was intended, therefore giving full class access.
// Note : It is VITALLY important that you should pass your 'this' pointer to
//        the lpParam parameter of the CreateWindow function if you wish to be
//        able to pass messages back to that app object.
//-----------------------------------------------------------------------------
LRESULT CALLBACK CGameApp::StaticWndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
    // If this is a create message, trap the 'this' pointer passed in and store it within the window.
    if ( Message == WM_CREATE ) SetWindowLong( hWnd, GWL_USERDATA, (LONG)((CREATESTRUCT FAR *)lParam)->lpCreateParams);

    // Obtain the correct destination for this message
    CGameApp *Destination = (CGameApp*)GetWindowLong( hWnd, GWL_USERDATA );
    
    // If the hWnd has a related class, pass it through
    if (Destination) return Destination->DisplayWndProc( hWnd, Message, wParam, lParam );
    
    // No destination found, defer to system...
    return DefWindowProc( hWnd, Message, wParam, lParam );
}

//-----------------------------------------------------------------------------
// Name : DisplayWndProc ()
// Desc : The display devices internal WndProc function. All messages being
//        passed to this function are relative to the window it owns.
//-----------------------------------------------------------------------------
LRESULT CGameApp::DisplayWndProc( HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam )
{
    float fAspect;

    // Determine message type
	switch (Message)
    {
		case WM_CREATE:
            break;
		
        case WM_CLOSE:
			PostQuitMessage(0);
			break;
		
        case WM_DESTROY:
			PostQuitMessage(0);
			break;
		
        case WM_SIZE:

            // Store new viewport sizes
            m_nViewWidth  = LOWORD( lParam );
            m_nViewHeight = HIWORD( lParam );
       
            // Rebuild the new frame buffer
            BuildFrameBuffer( m_nViewWidth, m_nViewHeight );

			break;

        case WM_KEYDOWN:

            // Which key was pressed?
			switch (wParam) 
            {
				case VK_ESCAPE:
					PostQuitMessage(0);
					return 0;

			} // End Switch

			break;

        case WM_COMMAND:

            // Process Menu Items
            switch( LOWORD(wParam) )
            {
				case ID_FILE_EXIT:
					PostQuitMessage(0);
				break;

			}

		default:
			return DefWindowProc(hWnd, Message, wParam, lParam);

    } // End Message Switch
    
    return 0;
}

//-----------------------------------------------------------------------------
// Name : BuildObjects ()
// Desc : Build our demonstration cube mesh, and the objects that instance it
//-----------------------------------------------------------------------------

bool CGameApp::BuildObjects()
{
	m_pMenu = new CMenu(m_hInstance, m_SpriteDC);
    // Success!
    return true;
}

//-----------------------------------------------------------------------------
// Name : FrameAdvance () (Private)
// Desc : Called to signal that we are now rendering the next frame.
//-----------------------------------------------------------------------------
void CGameApp::FrameAdvance()
{
  //  CMesh      *pMesh = NULL;
    TCHAR       lpszFPS[30];

    // Advance the timer
    m_Timer.Tick( 60.0f );
    

	//Process the user input
	ProcessInput();

    // Animate the two objects
    AnimateObjects();


	//Update and draw the menu, if neccessary
	//redundant, but saves time not having to pass the DC through a few functions
	if(m_pMenu->IsEnabled())
	{
		m_pMenu->Update(m_pBackBuffer->getDC());
	}

	if(m_StateManager.GetMenuState() == MenuStates::eMenu_Game)
	{
		//Logic
		GameLogic();

		//Render

	}

#	ifdef DEBUG_DRAW
	// Display Frame Rate
    m_Timer.GetFrameRate( lpszFPS );
    TextOut( m_pBackBuffer->getDC(), 5, 5, lpszFPS, strlen( lpszFPS ) ); 
#	endif



	m_pBackBuffer->present();
	m_nGameTime += m_Timer.GetTimeElapsed();
}


void CGameApp::GameLogic()
{

	EntityManager manager;
	Entity ent1, ent2, ent3;
	ent1.sID = "Entity 1";
	ent2.sID = "Entity 2";
	ent3.sID = "Entity 3";
	manager.AcceptEntityEntrance(ent1);
	manager.AcceptEntityEntrance(ent2);
	manager.AcceptEntityEntrance(ent3);



}



//Process the user input
void CGameApp::ProcessInput()
{
	static UCHAR pKeyBuffer[ 256 ];

    // Retrieve keyboard state
    if ( !GetKeyboardState( pKeyBuffer ) ) return;

    // Check the relevant keys
    if ( pKeyBuffer[ VK_UP    ] & 0xF0 )
	{
		if(m_StateManager.GetMenuState() == MenuStates::eMenu_Main)
		{
			m_pMenu->PreviousMenuItem(m_nGameTime);
		}
	}
    if ( pKeyBuffer[ VK_DOWN  ] & 0xF0 )
	{
		if(m_StateManager.GetMenuState() == MenuStates::eMenu_Main)
		{
			m_pMenu->NextMenuItem(m_nGameTime);
		}	
	}
    if ( pKeyBuffer[ VK_LEFT  ] & 0xF0 )
	{
	}
    if ( pKeyBuffer[ VK_RIGHT ] & 0xF0 )
	{
	}

	if( pKeyBuffer[ VK_RETURN ] & 0xF0 )
	{
		if(m_StateManager.GetMenuState() == MenuStates::eMenu_Main)
		{
			m_StateManager.RequestMenuStateChange(m_pMenu);
		}
	}

	if(pKeyBuffer[ VK_BACK ] & 0xF0 )
	{
		if(m_StateManager.GetMenuState() == MenuStates::eMenu_Game)
		{
			m_StateManager.RequestMenuStateChange(m_pMenu);
		}
	}
    

}


//-----------------------------------------------------------------------------
// Name : AnimateObjects () (Private)
// Desc : Animates the objects we currently have loaded.
//-----------------------------------------------------------------------------
void CGameApp::AnimateObjects()
{
	
}